package com.webcamtracker.tracker.example.pong;

import com.webcamtracker.move.tracking.TrackingEvent;
import com.webcamtracker.move.tracking.TrackingEventListener;
import com.webcamtracker.tracker.example.spatial.SpatialPanel;

import javax.swing.*;
import java.awt.*;
import java.util.Date;

public class PongPanel
        extends JPanel
        implements TrackingEventListener, Runnable {

    /**
     * The size of the canvas in witch the point varies.
     * The top-left location will be (-pointCanvasSize.getWidth/2 , -pointCanvasSize.getHeight/2)
     * The center location will be (0 , 0)
     * The bottom-right location will be (+pointCanvasSize.getWidth/2 , +pointCanvasSize.getHeight/2)
     */
    private final int canvasWidth;
    private final int canvasHeight;

    /**
     * Point Location > It varies:
     * on the "x" axis between (-pointCanvasSize.getWidth/2  : +pointCanvasSize.getWidth/2)
     * on the "y" axis between (-pointCanvasSize.getHeight/2 : +pointCanvasSize.getHeight/2)
     */
    private Point playerALocation;
    private Point playerBLocation;

    private static final int PORTAL_WIDTH = 560;
    private static final int PORTAL_HEIGHT = 400;

    private float speedX = .8F;
    private float speedY = .8F;
    private int posX = PORTAL_WIDTH / 2;
    private int posY = PORTAL_HEIGHT / 2;
    private int oldPosX = PORTAL_WIDTH / 2;
    private int oldPosY = PORTAL_HEIGHT / 2;
    private Date last = new Date();

    // status: 0 >> missed, 1 >> hit, 2 >> none
    private int statusA = 2;
    private int statusB = 2;
    private int playerAHits = 0;
    private int playerBHits = 0;
    private static final int PLAYER_A_POSITION = 10;
    private static final int PLAYER_B_POSITION = PORTAL_WIDTH - 10;
    private static final int PLAYER_HEIGHT = 40;
    private int turno = 2;
    private static int PLAYER_A = 1;
    private static int PLAYER_B = 2;

    public PongPanel(Dimension dimension) {
        setPreferredSize(new Dimension(PORTAL_WIDTH, PORTAL_HEIGHT));
        this.canvasWidth = (int) dimension.getWidth();
        this.canvasHeight = (int) dimension.getHeight();
        this.playerALocation = new Point(PLAYER_A_POSITION, PORTAL_HEIGHT / 2);
        this.playerBLocation = new Point(PLAYER_B_POSITION, PORTAL_HEIGHT / 2);
        initTrackingPads();
        (new Thread(this)).start();
    }

    private void initTrackingPads() {
        SpatialPanel.TrackingPanel.buildTrackingFrame(this, "playerA");
        SpatialPanel.TrackingPanel.buildTrackingFrame(this, "playerB");
    }

    public void paint(Graphics g) {
        Graphics2D graphics = (Graphics2D) g;

        paintStatus(graphics);

        paintPlayer(graphics, playerALocation);
        paintPlayer(graphics, playerBLocation);

        paintBall(graphics);

        graphics.drawString("Player A : " + playerAHits, 1, 10);
        graphics.drawString("Player B : " + playerBHits, PORTAL_WIDTH - 80, 10);
    }

    private void paintStatus(Graphics2D graphics) {
        int status = 2;
        int playerPosition = -1;

        if (statusA != 2) {
            status = statusA;
            playerPosition = (int) playerALocation.getX();
        } else if (statusB != 2) {
            status = statusB;
            playerPosition = (int) playerBLocation.getX();
        }

        if (status != 2) {
            graphics.clearRect(0, 0, 15, PORTAL_HEIGHT);
            graphics.clearRect(PORTAL_WIDTH - 15, 0, 15, PORTAL_HEIGHT);
            graphics.clearRect(10,0,70,20);
            graphics.clearRect(PORTAL_WIDTH - 80, 0,80,20);
            final Color original = graphics.getColor();
            graphics.setColor(status == 0 ? Color.RED : Color.GREEN);
            graphics.fillRect(playerPosition == PLAYER_A_POSITION ? 0 : PORTAL_WIDTH - 5, 0, 5, PORTAL_HEIGHT);
            graphics.setColor(original);

        }

    }

    private void computeNewBallLocation() {
        Date now = new Date();

        long elapsedTime = now.getTime() - last.getTime();

        oldPosX = posX;
        oldPosY = posY;

        float deltaX = speedX * ((float) elapsedTime / 10);
        float deltaY = speedY * ((float) elapsedTime / 10);

        if (turno == PLAYER_A && posX < PLAYER_HEIGHT &&
                Math.abs(playerALocation.getX() - posX) < 10 && Math.abs(playerALocation.getY() - posY) < PLAYER_HEIGHT) {
            System.out.println("PLAYER A HIT!");
            playerAHits++;
            statusA = 1;
            statusB = 2;
            deltaX = (float) (deltaX - playerALocation.getX()) * -1;
            speedX *= -1;
            turno = PLAYER_B;
        } else if (turno == PLAYER_B && posX > (PORTAL_WIDTH - PLAYER_HEIGHT) &&
                Math.abs(playerBLocation.getX() - posX) < 10 && Math.abs(playerBLocation.getY() - posY) < PLAYER_HEIGHT) {
            System.out.println("PLAYER B HIT!");
            playerBHits++;
            statusA = 2;
            statusB = 1;
            deltaX = (float) (playerBLocation.getX() + deltaX - PORTAL_WIDTH) * -1;
            speedX *= -1;
            turno = PLAYER_A;
        }

        if (turno == PLAYER_B && posX + deltaX > PORTAL_WIDTH) {
            System.out.println("PLAYER B MISSED!");
            deltaX = (posX + deltaX - PORTAL_WIDTH) * -1;
            speedX *= -1;
            statusA = 2;
            statusB = 0;
             turno = PLAYER_A;
        } else if (turno == PLAYER_A && posX + deltaX < 0) {
            System.out.println("PLAYER A MISSED!");
            deltaX = (deltaX - posX) * -1;
            speedX *= -1;
            statusA = 0;
            statusB = 2;
            turno = PLAYER_B;
        }

        if (posY + deltaY > PORTAL_HEIGHT) {
            deltaY = (posY + deltaY - PORTAL_HEIGHT) * -1;
            speedY *= -1;
        } else if (posY + deltaY < 0) {
            deltaY = (deltaY - posY) * -1;
            speedY *= -1;
        }

        posX += deltaX;
        posY += deltaY;

        last = now;
    }

    private void paintBall(Graphics2D graphics) {
        final int ballSize = 15;

        // Erase the ball
        graphics.clearRect(oldPosX - (ballSize / 2), oldPosY - (ballSize / 2), ballSize, ballSize);

        // Draw the ball
        graphics.fillOval(posX - (ballSize / 2), posY - (ballSize / 2), ballSize, ballSize);
    }

    private void paintPlayer(Graphics2D graphics, Point playerLocation) {
        final int playerWidth = 10;
        final int playerHeight = PLAYER_HEIGHT;

        // Erase all the corresponding side
        graphics.clearRect(playerLocation.x - playerWidth / 2, 0, playerWidth, PORTAL_HEIGHT);

        // Draw the pong...
        graphics.fillRect(playerLocation.x - (playerWidth / 2),
                playerLocation.y - (playerHeight / 2),
                playerWidth, playerHeight);
    }

    public void trackingEventDispatched(TrackingEvent event) {
        if (event.getId().equals("playerA")) {
            setPlayerLocation(playerALocation, event);
        } else if (event.getId().equals("playerB")) {
            setPlayerLocation(playerBLocation, event);
        }
    }

    private void setPlayerLocation(Point playerLocation, TrackingEvent event) {
        final float ratio = (float) PORTAL_HEIGHT / (float) canvasHeight;
        playerLocation.setLocation(playerLocation.x, event.getPoint().getY() * ratio);
    }


    public void run() {
        while (true) {
            try {
                computeNewBallLocation();
                repaint();
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}